#include "udpclient.h"
#include<qdebug.h>
#pragma pack(1)

UDPClient::UDPClient()
{
    char ip[] = "47.113.197.15";
    UDPClient(ip, 54321);
}

UDPClient::UDPClient(char* ip, int port) : srejoin(true),cport(pport)
{
    //分配空间
    rdata = new char[RBUF_SIZE];
    sdata = new char[SBUF_SIZE];
    rdata_ = new char[RBUF_SIZE];
    sdata_ = new char[SBUF_SIZE];

    //初始化
    memset(rdata, 0, RBUF_SIZE);
    memset(sdata, 0, SBUF_SIZE);
    memset(rdata_, 0, RBUF_SIZE);
    memset(sdata_, 0, SBUF_SIZE);

    //初始化服务器的socket地址
    sersin.sin_family = AF_INET;//协议版本
    sersin.sin_addr.S_un.S_addr = inet_addr(ip);//服务器IP地址
    sersin.sin_port = htons(port);//端口号0-65535

    //初始化sinlen
    sinlen = sizeof(SOCKADDR_IN);

    //创建客户端与其他方交流的套接字
    cli = CreateSocketUDP();

    //talk to ser to get the sockaddr of peer
    peer = TalkServer();

    //绑定peer到cli也就是指定cli发收数据都是与peer
    Connect();

}

UDPClient::~UDPClient()
{
    //关闭socket
    closesocket(cli);
    qDebug() << "被释放拉woc";
    //释放空间
    delete[]rdata;
    delete[]sdata;
    delete[]rdata_;
    delete[]sdata_;
}

SOCKADDR_IN UDPClient::TalkServer()
{
    SOCKADDR_IN peersin;
    //定期发送join/rejoin包给服务器，发送rejoin是防止原NAT映射被改变
    memset(sdata, 0, SBUF_SIZE);
    sdata[0] = 'j', sdata[1] = 'o', sdata[2] = 'i', sdata[3] = 'n';
    SendToSer();
    thread th1(&UDPClient::ReceSOCKADDR_IN,this);
    memset(sdata, 0, SBUF_SIZE);
    sdata[0] = 'r', sdata[1] = 'e', sdata[2] = 'j', sdata[3] = 'o';
    while (srejoin) {
        Sleep(3000);
        SendToSer();
    }
    memcpy(&peersin, rdata, sinlen);
    th1.join();
    return peersin;
}

int UDPClient::Wsa()
{
    //请求协议版本
    WSADATA wsaData;
    int err = WSAStartup(MAKEWORD(2, 2), &wsaData);		//调用Winsock2.2版本
    if (err != 0) printf("WSAStartup失败\n");
    //检查版本是否正确
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("请求协议版本失败\n");
        return -1;
    }
    printf("请求协议版本成功\n");
    return 0;
}

SOCKET UDPClient::CreateSocketUDP()
{
    Wsa();
    //创建socket
    SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);//第二个参数选择UDP/TCP
    if (-1 == clientSocket)
    {
        printf("创建socket失败\n");
        return -1;
    }
    printf("创建socket成功\n");

    //设置本地socket地址信息
    SOCKADDR_IN caddr = { 0 };
    caddr.sin_family = AF_INET;//协议版本
    caddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    caddr.sin_port = htons(pport);//端口号1024-65535

    //绑定到socket
    int r = ::bind(clientSocket, (sockaddr*)&caddr, sizeof(caddr));//绑定
    //检查
    if (-1 == r)
    {
        printf("bind失败\n");
        WSACleanup();
        return -1;
    }
    printf("bind成功\n");


    return clientSocket;	//返回创建的socket连接
}

void UDPClient::ReceSOCKADDR_IN()
{
    memset(rdata, 0, RBUF_SIZE);
    //读取server数据,有数据更新才读取，否则阻塞
    recvfrom(cli, rdata, SBUF_SIZE, 0, NULL, 0);
    srejoin = false;
}

void UDPClient::ReceData()
{
    memset(rdata, 0, RBUF_SIZE);
    //读取server数据,有数据更新才读取，否则阻塞
    recvfrom(cli, rdata, SBUF_SIZE, 0, NULL, 0);
    if (rdata[0] == 'x' && rdata[1] == 't') {
        return;
    }
    else {
        memset(rdata_, 0, RBUF_SIZE);
        memcpy(rdata_, rdata, RBUF_SIZE);
    }
}

point UDPClient::Rece()
{
    point x;
    memcpy(&x, rdata_, sizeof(point));
    memset(rdata_, 0, RBUF_SIZE);
    return x;
}

void UDPClient::Connect()
{
    //连接另一台主机
    int r = connect(cli, (sockaddr*)&peer, sizeof(peer));
    if (-1 == r)
    {
        printf("连接主机失败\n");
        return;
    }
    printf("连接主机成功\n");

}

void UDPClient::SendData()
{
    memset(sdata, 0, SBUF_SIZE);
    memcpy(sdata, sdata_, SBUF_SIZE);
    memset(sdata_, 0, SBUF_SIZE);
    if (sdata[0]=='0') {	//无待发送的数据则发送心跳包防止路由断开
        sdata[0] = 'x', sdata[1] = 't';
    }
    sendto(cli, sdata, SBUF_SIZE, 0, NULL, 0);
}

void UDPClient::Send(point x)
{
    memset(sdata_, 0, SBUF_SIZE);
    memcpy(sdata_, &x, sizeof(point));
}


void UDPClient::SendToSer()
{
    sendto(cli, sdata, SBUF_SIZE, 0, (sockaddr*)&sersin, sinlen);
}
